package com.kexilo.messaging.domain;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 消息抽象模型
 * 不依赖任何具体的消息实体，完全解耦
 * 
 * @author Kexilo
 */
public class Message implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    /**
     * 消息ID
     */
    private String messageId;
    
    /**
     * 消息类型
     */
    private String messageType;
    
    /**
     * 消息主题/队列名
     */
    private String topic;
    
    /**
     * 消息标签
     */
    private String tag;
    
    /**
     * 消息内容
     */
    private String payload;
    
    /**
     * 消息头信息
     */
    private Map<String, Object> headers = new HashMap<>();
    
    /**
     * 消息优先级
     */
    private Integer priority;
    
    /**
     * 延迟时间（毫秒）
     */
    private Long delayTime;
    
    /**
     * 超时时间（毫秒）
     */
    private Long timeout;
    
    /**
     * 重试次数
     */
    private Integer retryCount;
    
    /**
     * 最大重试次数
     */
    private Integer maxRetries;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 发送时间
     */
    private LocalDateTime sendTime;
    
    /**
     * 处理时间
     */
    private LocalDateTime processTime;
    
    /**
     * 完成时间
     */
    private LocalDateTime completeTime;
    
    /**
     * 消息状态
     */
    private MessageStatus status;
    
    /**
     * 发送者ID
     */
    private String senderId;
    
    /**
     * 接收者ID
     */
    private String receiverId;
    
    /**
     * 业务ID
     */
    private String businessId;
    
    /**
     * 业务类型
     */
    private String businessType;
    
    /**
     * 链路追踪ID
     */
    private String traceId;
    
    /**
     * 消息来源
     */
    private String source;
    
    /**
     * 错误信息
     */
    private String errorMessage;
    
    /**
     * 错误次数
     */
    private Integer errorCount;
    
    /**
     * 消息状态枚举
     */
    public enum MessageStatus {
        CREATED,     // 已创建
        PENDING,     // 待发送
        SENT,        // 已发送
        DELIVERED,   // 已投递
        PROCESSING,  // 处理中
        PROCESSED,   // 已处理
        COMPLETED,   // 已完成
        FAILED,      // 处理失败
        DEAD_LETTER, // 死信
        EXPIRED      // 已过期
    }
    
    /**
     * 消息优先级枚举
     */
    public enum MessagePriority {
        LOW(1),
        NORMAL(5),
        HIGH(8),
        URGENT(10);
        
        private final int value;
        
        MessagePriority(int value) {
            this.value = value;
        }
        
        public int getValue() {
            return value;
        }
    }
    
    // 构造器
    public Message() {
        this.messageId = generateMessageId();
        this.createTime = LocalDateTime.now();
        this.status = MessageStatus.CREATED;
        this.retryCount = 0;
        this.errorCount = 0;
        this.priority = MessagePriority.NORMAL.getValue();
    }
    
    public Message(String messageType, String topic, String payload) {
        this();
        this.messageType = messageType;
        this.topic = topic;
        this.payload = payload;
    }
    
    // Builder模式构造器
    public static Builder builder() {
        return new Builder();
    }
    
    public static class Builder {
        private Message message = new Message();
        
        public Builder messageId(String messageId) {
            message.messageId = messageId;
            return this;
        }
        
        public Builder messageType(String messageType) {
            message.messageType = messageType;
            return this;
        }
        
        public Builder topic(String topic) {
            message.topic = topic;
            return this;
        }
        
        public Builder tag(String tag) {
            message.tag = tag;
            return this;
        }
        
        public Builder payload(String payload) {
            message.payload = payload;
            return this;
        }
        
        public Builder headers(Map<String, Object> headers) {
            if (headers != null) {
                message.headers.putAll(headers);
            }
            return this;
        }
        
        public Builder header(String key, Object value) {
            message.headers.put(key, value);
            return this;
        }
        
        public Builder priority(Integer priority) {
            message.priority = priority;
            return this;
        }
        
        public Builder priority(MessagePriority priority) {
            message.priority = priority.getValue();
            return this;
        }
        
        public Builder delayTime(Long delayTime) {
            message.delayTime = delayTime;
            return this;
        }
        
        public Builder timeout(Long timeout) {
            message.timeout = timeout;
            return this;
        }
        
        public Builder maxRetries(Integer maxRetries) {
            message.maxRetries = maxRetries;
            return this;
        }
        
        public Builder senderId(String senderId) {
            message.senderId = senderId;
            return this;
        }
        
        public Builder receiverId(String receiverId) {
            message.receiverId = receiverId;
            return this;
        }
        
        public Builder businessId(String businessId) {
            message.businessId = businessId;
            return this;
        }
        
        public Builder businessType(String businessType) {
            message.businessType = businessType;
            return this;
        }
        
        public Builder traceId(String traceId) {
            message.traceId = traceId;
            return this;
        }
        
        public Builder source(String source) {
            message.source = source;
            return this;
        }
        
        public Message build() {
            if (message.messageId == null) {
                message.messageId = generateMessageId();
            }
            if (message.createTime == null) {
                message.createTime = LocalDateTime.now();
            }
            if (message.status == null) {
                message.status = MessageStatus.CREATED;
            }
            return message;
        }
    }
    
    /**
     * 生成消息ID
     */
    private static String generateMessageId() {
        return "MSG" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
    }
    
    /**
     * 增加重试次数
     */
    public void incrementRetryCount() {
        this.retryCount = (this.retryCount == null) ? 1 : this.retryCount + 1;
    }
    
    /**
     * 增加错误次数
     */
    public void incrementErrorCount() {
        this.errorCount = (this.errorCount == null) ? 1 : this.errorCount + 1;
    }
    
    /**
     * 是否可以重试
     */
    public boolean canRetry() {
        if (maxRetries == null || retryCount == null) {
            return true;
        }
        return retryCount < maxRetries;
    }
    
    /**
     * 是否已过期
     */
    public boolean isExpired() {
        if (timeout == null || createTime == null) {
            return false;
        }
        return LocalDateTime.now().isAfter(createTime.plusNanos(timeout * 1_000_000));
    }
    
    /**
     * 是否为延迟消息
     */
    public boolean isDelayMessage() {
        return delayTime != null && delayTime > 0;
    }
    
    /**
     * 是否为高优先级消息
     */
    public boolean isHighPriority() {
        return priority != null && priority >= MessagePriority.HIGH.getValue();
    }
    
    /**
     * 获取消息大小（字节）
     */
    public int getMessageSize() {
        int size = 0;
        if (payload != null) {
            size += payload.getBytes().length;
        }
        if (headers != null) {
            size += headers.toString().getBytes().length;
        }
        return size;
    }
    
    /**
     * 复制消息
     */
    public Message copy() {
        Message copy = new Message();
        copy.messageId = this.messageId;
        copy.messageType = this.messageType;
        copy.topic = this.topic;
        copy.tag = this.tag;
        copy.payload = this.payload;
        copy.headers = new HashMap<>(this.headers);
        copy.priority = this.priority;
        copy.delayTime = this.delayTime;
        copy.timeout = this.timeout;
        copy.retryCount = this.retryCount;
        copy.maxRetries = this.maxRetries;
        copy.createTime = this.createTime;
        copy.sendTime = this.sendTime;
        copy.processTime = this.processTime;
        copy.completeTime = this.completeTime;
        copy.status = this.status;
        copy.senderId = this.senderId;
        copy.receiverId = this.receiverId;
        copy.businessId = this.businessId;
        copy.businessType = this.businessType;
        copy.traceId = this.traceId;
        copy.source = this.source;
        copy.errorMessage = this.errorMessage;
        copy.errorCount = this.errorCount;
        return copy;
    }
    
    // Getters and Setters
    public String getMessageId() { return messageId; }
    public void setMessageId(String messageId) { this.messageId = messageId; }
    
    public String getMessageType() { return messageType; }
    public void setMessageType(String messageType) { this.messageType = messageType; }
    
    public String getTopic() { return topic; }
    public void setTopic(String topic) { this.topic = topic; }
    
    public String getTag() { return tag; }
    public void setTag(String tag) { this.tag = tag; }
    
    public String getPayload() { return payload; }
    public void setPayload(String payload) { this.payload = payload; }
    
    public Map<String, Object> getHeaders() { return headers; }
    public void setHeaders(Map<String, Object> headers) { this.headers = headers; }
    
    public Integer getPriority() { return priority; }
    public void setPriority(Integer priority) { this.priority = priority; }
    
    public Long getDelayTime() { return delayTime; }
    public void setDelayTime(Long delayTime) { this.delayTime = delayTime; }
    
    public Long getTimeout() { return timeout; }
    public void setTimeout(Long timeout) { this.timeout = timeout; }
    
    public Integer getRetryCount() { return retryCount; }
    public void setRetryCount(Integer retryCount) { this.retryCount = retryCount; }
    
    public Integer getMaxRetries() { return maxRetries; }
    public void setMaxRetries(Integer maxRetries) { this.maxRetries = maxRetries; }
    
    public LocalDateTime getCreateTime() { return createTime; }
    public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
    
    public LocalDateTime getSendTime() { return sendTime; }
    public void setSendTime(LocalDateTime sendTime) { this.sendTime = sendTime; }
    
    public LocalDateTime getProcessTime() { return processTime; }
    public void setProcessTime(LocalDateTime processTime) { this.processTime = processTime; }
    
    public LocalDateTime getCompleteTime() { return completeTime; }
    public void setCompleteTime(LocalDateTime completeTime) { this.completeTime = completeTime; }
    
    public MessageStatus getStatus() { return status; }
    public void setStatus(MessageStatus status) { this.status = status; }
    
    public String getSenderId() { return senderId; }
    public void setSenderId(String senderId) { this.senderId = senderId; }
    
    public String getReceiverId() { return receiverId; }
    public void setReceiverId(String receiverId) { this.receiverId = receiverId; }
    
    public String getBusinessId() { return businessId; }
    public void setBusinessId(String businessId) { this.businessId = businessId; }
    
    public String getBusinessType() { return businessType; }
    public void setBusinessType(String businessType) { this.businessType = businessType; }
    
    public String getTraceId() { return traceId; }
    public void setTraceId(String traceId) { this.traceId = traceId; }
    
    public String getSource() { return source; }
    public void setSource(String source) { this.source = source; }
    
    public String getErrorMessage() { return errorMessage; }
    public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
    
    public Integer getErrorCount() { return errorCount; }
    public void setErrorCount(Integer errorCount) { this.errorCount = errorCount; }
    
    @Override
    public String toString() {
        return "Message{" +
                "messageId='" + messageId + '\'' +
                ", messageType='" + messageType + '\'' +
                ", topic='" + topic + '\'' +
                ", status=" + status +
                ", priority=" + priority +
                ", retryCount=" + retryCount +
                ", createTime=" + createTime +
                '}';
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Message message = (Message) obj;
        return messageId != null ? messageId.equals(message.messageId) : message.messageId == null;
    }
    
    @Override
    public int hashCode() {
        return messageId != null ? messageId.hashCode() : 0;
    }
}
